package model

import (
	"common/helper"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	g "github.com/doug-martin/goqu/v9"
	"github.com/doug-martin/goqu/v9/exp"
	"github.com/go-redis/redis/v8"
	"github.com/valyala/fasthttp"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type TblBanner struct {
	Id          string `db:"id" json:"id"`
	Title       string `db:"title" json:"title"`               //标题
	RedirectUrl string `db:"redirect_url" json:"redirect_url"` //跳转地址,多个地址用逗号分隔
	Images      string `db:"images" json:"images"`             //图片路径
	Seq         int    `db:"seq" json:"seq"`                   //排序
	UrlType     int    `db:"url_type" json:"url_type"`         //链接类型 1站内 2站外
	UpdatedName string `db:"updated_name" json:"updated_name"` //更新人name
	UpdatedUid  string `db:"updated_uid" json:"updated_uid"`   //更新人id
	UpdatedAt   uint32 `db:"updated_at" json:"updated_at"`     //更新时间
	State       string `db:"state" json:"state"`               //1待发布 2开启 3停用
	CreatedAt   uint32 `db:"created_at" json:"created_at"`
}

type BannerData struct {
	D []TblBanner `json:"d"`
	T int64       `json:"total"`
	S uint        `json:"s"`
}

type bannerRawData struct {
	Status bool        `json:"status"`
	D      []TblBanner `json:"data"`
}

func BannerUpload(fctx *fasthttp.RequestCtx) (string, error) {

	fh, err := fctx.FormFile("uploadfile")
	if err != nil {
		//ctx.SetBodyString("Upload error")
		return "", errors.New(helper.ParamNull)
	}

	ext := filepath.Ext(fh.Filename)
	if ext != ".webp" && ext != ".gif" && ext != ".jpg" && ext != ".png" {

		return "", errors.New(helper.ParamNull)
	}
	//timeout := time.Minute * 10
	//ctx, cancel := context.WithTimeout(ctx, time.Second*20)
	//defer cancel()

	/*
		ss, err := session.NewSession(&aws.Config{
			Region:      aws.String("sa-east-1"),
			Credentials: credentials.NewStaticCredentials(meta.MerchantS3.AccessKeyID, meta.MerchantS3.SecretAccessKey, ""),
		})

		sess := session.Must(ss, err)

	*/

	// Create a new instance of the service's client with a Session.
	// Optional aws.Config values can also be provided as variadic arguments
	// to the New function. This option allows you to provide service
	// specific configuration.
	//svc := s3.New(sess)
	//var cancelFn func()
	//if timeout > 0 {
	//ctx, cancelFn = context.WithTimeout(ctx, timeout)
	//}
	//if cancelFn != nil {
	//defer cancelFn()
	//}
	uploadDir := "/www/wwwroot/imagefile/uploadfile/"
	filename := fmt.Sprintf("%d"+ext, fctx.Time().UnixMilli())
	filename = filepath.Join(uploadDir, fmt.Sprintf("banner_%s", filename))

	f, err := fh.Open()
	if err != nil {

		return "", err
	}
	defer f.Close()

	outFile, err := os.Create(filename)
	if err != nil {

		return "", err
	}
	defer outFile.Close() // 确保在函数返回前关闭文件

	// 将上传的文件内容拷贝到打开的文件中
	if _, err := io.Copy(outFile, f); err != nil {
		return "", err
	}

	domain := meta.BannerUrl
	//_, err = svc.PutObjectWithContext(fctx, &s3.PutObjectInput{
	//	Bucket: aws.String(meta.MerchantS3.Bucket),
	//	Key:    aws.String(filename),
	//	Body:   f,
	//	ACL:    aws.String("public-read"),
	//})
	//if err != nil {
	//	if aerr, ok := err.(awserr.Error); ok && aerr.Code() == request.CanceledErrorCode {
	//		// If the SDK can determine the request or retry delay was canceled
	//		// by a context the CanceledErrorCode error code will be returned.
	//		//fmt.Fprintf(os.Stderr, "upload canceled due to timeout, %s\n", err.Error())
	//		return "", fmt.Errorf("upload canceled due to timeout, %s", err.Error())
	//	} else {
	//		//fmt.Fprintf(os.Stderr, "failed to upload object, %s\n", err.Error())
	//		return "", fmt.Errorf("failed to upload object, %s", err.Error())
	//	}
	//	//os.Exit(1)
	//}
	ret_url := strings.Replace(filename, "/www/wwwroot/imagefile", domain, -1)

	return ret_url, nil
}

func BannerFlushCache() error {

	var record []TblBanner
	ex := g.Ex{
		"state": "2",
	}
	query, _, _ := dialect.From("tbl_banner").Select(colsBanner...).Where(ex).Order(g.C("seq").Asc()).ToSQL()

	err := meta.MerchantDB.Select(&record, query)
	if err != nil {
		if err != sql.ErrNoRows {
			err = pushLog(fmt.Errorf("%s,[%s]", err.Error(), query), "数据库错误")
		}
		return err
	}

	if len(record) == 0 {
		meta.MerchantRedis.Del(ctx, "banner").Err()
		return nil
	}

	for i, _ := range record {
		record[i].UpdatedUid = ""
		record[i].UpdatedName = ""
		record[i].UpdatedAt = 0
		record[i].CreatedAt = 0
	}
	recs := bannerRawData{
		Status: true,
		D:      record,
	}

	b, err := json.Marshal(recs)
	if err != nil {

		return err
	}

	pipe := meta.MerchantRedis.Pipeline()
	defer pipe.Close()
	pipe.Del(ctx, "banner")
	pipe.Set(ctx, "banner", b, 0)
	pipe.Persist(ctx, "banner")
	_, err = pipe.Exec(ctx)
	if err != nil && err != redis.Nil {
		fmt.Println(err.Error())
		return nil
	}

	return nil
}

func BannerInsert(data TblBanner) error {

	query, _, _ := dialect.Insert("tbl_banner").Rows(&data).ToSQL()
	_, err := meta.MerchantDB.Exec(query)
	if err != nil {
		return pushLog(fmt.Errorf("%s,[%s]", err.Error(), query), "数据库错误")
	}
	BannerFlushCache()
	return nil
}

func BannerUpdate(id string, record g.Record) error {

	ex := g.Ex{
		"id": id,
	}

	query, _, _ := dialect.Update("tbl_banner").Set(record).Where(ex).ToSQL()
	_, err := meta.MerchantDB.Exec(query)
	if err != nil {
		return pushLog(fmt.Errorf("%s,[%s]", err.Error(), query), "数据库错误")
	}

	BannerFlushCache()

	return nil
}

func BannerUpdateState(id string, state int) error {

	ex := g.Ex{
		"id": id,
	}

	record := g.Record{
		"state": state,
	}
	query, _, _ := dialect.Update("tbl_banner").Set(record).Where(ex).ToSQL()
	_, err := meta.MerchantDB.Exec(query)
	if err != nil {
		return pushLog(fmt.Errorf("%s,[%s]", err.Error(), query), "数据库错误")
	}

	BannerFlushCache()

	return nil
}

func BannerDelete(id string) error {

	ex := g.Ex{
		"id": id,
	}
	query, _, _ := dialect.Delete("tbl_banner").Where(ex).ToSQL()
	_, err := meta.MerchantDB.Exec(query)
	if err != nil {
		return pushLog(fmt.Errorf("%s,[%s]", err.Error(), query), "数据库错误")
	}
	BannerFlushCache()
	return nil
}

func BannerList(page, pageSize uint, startTime, endTime string, ex g.Ex) (BannerData, error) {

	data := BannerData{}

	if startTime != "" && endTime != "" {
		st, err := time.ParseInLocation("2006-01-02 15:04:05", startTime, time.Local)
		if err != nil {
			return data, errors.New(helper.TimeTypeErr)
		}

		et, err := time.ParseInLocation("2006-01-02 15:04:05", endTime, time.Local)
		if err != nil {
			return data, errors.New(helper.TimeTypeErr)
		}

		ex["created_at"] = g.Op{"between": exp.NewRangeVal(st.Unix(), et.Unix())}
	}

	t := dialect.From("tbl_banner")
	if page >= 1 {
		query, _, _ := t.Select(g.COUNT(1)).Where(ex).ToSQL()
		err := meta.MerchantDB.Get(&data.T, query)
		if err != nil {
			return data, pushLog(fmt.Errorf("%s,[%s]", err.Error(), query), "数据库错误")
		}

		if data.T == 0 {
			return data, nil
		}
	}

	data.S = pageSize
	offset := (page - 1) * pageSize
	query, _, _ := t.Select(colsBanner...).Where(ex).Order(g.C("seq").Asc()).Offset(offset).Limit(pageSize).ToSQL()
	err := meta.MerchantDB.Select(&data.D, query)
	if err != nil && err != sql.ErrNoRows {
		return data, pushLog(fmt.Errorf("%s,[%s]", err.Error(), query), "数据库错误")
	}

	return data, nil
}
